﻿package extremefx.i18n {
	import extremefx.DateTime;
	import extremefx.i18n.Calendar;	

	/**
	 * @author Marcelo Volmaro
	 * <p>This is the Gregorian calendar.</p>
	 * <p>The Gregorian calendar supports only the Common Era from the Gregorian year 1 to the Gregorian year 9999.</p>
	 */
	public class GregorianCalendar extends Calendar {
		/**
		 * The era number for the Common Era (C.E.) or Anno Domini (A.D.)
		 */
		private static const _AD_ERA:uint = 1;
		
		private static const _MIN:DateTime = new DateTime (1, 1, 1, 0, 0, 0);
		private static const _MAX:DateTime = new DateTime (9999, 12, 31, 11, 59, 59);
		
		/**
		 * The property stores the <code>GregorianCalendarTypes</code>
		 */
		public var calendarType:uint;
		
		public function GregorianCalendar(pType:uint = 1) {
			super();
			calendarType = pType;
			_abbrEraNames = ["AD"];
			_eraNames = ["A.D."];
			if (_twoDigitYearMax == 99) _twoDigitYearMax = 2029;
		}
		
		public override function get minSupportedDateTime():DateTime {
			return _MIN;
		}
		
		public override function get maxSupportedDateTime():DateTime {
			return _MAX;
		}
		
		public override function get algorithmType():uint {
			return CalendarAlgorithmType.SOLAR;
		}
		
		/**
		 * Overridden. Gives the eras supported by the Gregorian calendar as an array of integers.
		 */
		public override function get eras():Array {
			return [_AD_ERA];  
		}
		
		public override function set twoDigitYearMax(pYear:int):void{ 
			_argumentInRange ("value", pYear, 100, _getMaxYear());
			_twoDigitYearMax = pYear;
		}
		
		/**
		 * Overridden. Adds months to a given date.
		 */
		public override function addMonths(pTime:DateTime, pMonths:int):DateTime {
			return CCGregorian._addMonths(pTime, pMonths);
		}
		
		/**
		 * Overridden. Adds years to a given date.
		 */
		public override function addYears(pTime:DateTime, pYears:int):DateTime {
			return CCGregorian._addYears(pTime, pYears);
		}
		
		/**
		 * Overridden. Gets the day of the month from <code>pTime</code>
		 */
		public override function getDayOfMonth(pTime:DateTime):int {
			return CCGregorian._getDayOfMonth(pTime);
		}
		
		/**
		 * Overridden. Gets the day of the week from the specified date.
		 */
		public override function getDayOfWeek(pTime:DateTime):int {
			return CCFixed._dayOfWeek(CCFixed._fromDateTime(pTime));
		}
		
		/**
		 * Overridden. Gives the number of the day in the year.
		 */
		public override function getDayOfYear(pTime:DateTime):int {
			return CCGregorian._getDayOfYear(pTime);
		}
		
		/**
		 * Overridden. Gives the number of days in the specified month of the given year and era.
		 */
		public override function getDaysInMonth(pYear:int, pMonth:int, pEra:int = -1):int {
			_checkYME(pYear, pMonth, [pEra]);
			return CCGregorian._getDaysInMonth(pYear, pMonth);
		}
		
		/**
		 * Overridden. Gives the number of days of the specified year of the given era. 
		 */
		public override function getDaysInYear(pYear:int, pEra:int = -1):int {
			_checkYE(pYear, [pEra]);
			return CCGregorian._getDaysInYear(pYear);
		}
		
		public override function getEra(pTime:DateTime):int {
			return _AD_ERA;
		}
		
		public override function getLeapMonth(pYear:int, pEra:int = -1):int {
			return 0;
		}
		
		/**
		 * Overridden. Gives the number of the month of the specified date.
		 */
		public override function getMonth(pTime:DateTime):int {
			return CCGregorian._getMonth(pTime);
		}
		
		/**
		 * Overridden. Gives the number of months in the specified year and era.
		 */
		public override function getMonthsInYear(pYear:int, pEra:int = -1):int {
			_checkYE(pYear, [pEra]);
			return 12;
		}
		
		/**
		 * Overridden. Gives the number of the year of the specified date.
		 */
		public override function getYear(pTime:DateTime):int {
			return CCGregorian._getYear(pTime);
		}
		
		/**
		 * Overridden. Tells whether the given day is a leap day.
		 */
		public override function isLeapDay(pYear:int, pMonth:int, pDay:int, pEra:int= -1):Boolean {
			_checkYMDE(pYear, pMonth, pDay, [pEra]);
			return CCGregorian._isLeapDay(pYear, pMonth, pDay);
		}
		
		public override function isLeapMonth(pYear:int, pMonth:int, pEra:int= -1):Boolean {
			_checkYME(pYear, pMonth, [pEra]);
			return false;
		}
		
		public override function isLeapYear(pYear:int, pEra:int= -1):Boolean {
			_checkYE(pYear, [pEra]);
			return CCGregorian._isLeapYear(pYear);
		}
		
		public override function toDateTime(pYear:int, pMonth:int, pDay:int, pHour:int, pMinute:int, pSecond:int, pMSecond:int, pEra:int = -1):DateTime {
			_checkYMDE(pYear, pMonth, pDay, [pEra]);
			_checkHMSM(pHour, pMinute, pSecond, pMSecond);
			return CCGregorian._toDateTime(pYear, pMonth, pDay, pHour, pMinute, pSecond, pMSecond);
		}
		
		protected function _checkEra(pEra:Array):void {
			var era:int = pEra[0];
			if (era == -1) era = _AD_ERA;
			if (era != _AD_ERA) throw new ArgumentError("Era value was not valid.");
			pEra[0] = era;
		}
		
		protected override function _checkYE(pYear:int, pEra:Array):void {
			_checkEra(pEra);
			_argumentInRange("year", pYear, 1, 9999);
		}
		
		protected function _checkYME(pYear:int, pMonth:int, pEra:Array):void {
			_checkYE(pYear, pEra);
			if (pMonth < 1 || pMonth > 12) throw new RangeError("pMonth", "Month must be between one and twelve.");
		}
		
		protected function _checkYMDE(pYear:int, pMonth:int, pDay:int, pEra:Array):void {
			_checkYME(pYear, pMonth, pEra);
			_argumentInRange("pDay", pDay, 1, getDaysInMonth(pYear, pMonth, pEra[0]));
		}
		
	}
}
